﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using WatsonWebsocket;
using WWDemon.Log;
using WWDemon.Model;

namespace WWDemon.Core.WebSocket
{
    public class WebSocketServer
    {
        private readonly ConcurrentDictionary<string, ClientInfo> clients = new ConcurrentDictionary<string, ClientInfo>();

        public event SocketAccept OnAccept;

        public event SocketClose OnClose;

        public event SockettReceivePC OnReceivePC;
        public event SockettReceiveWeb OnReceiveWeb;


        public delegate void SocketAccept(string ipProt);

        public delegate void SocketClose(string ipProt);

        public delegate void SockettReceivePC(string ipProt, CommondPC receiveData);
        public delegate void SockettReceiveWeb(string ipProt, CommondWeb receiveData);

        private WatsonWsServer server;

        public WebSocketServer(string ip,int prot)
        {
            server = new WatsonWsServer(ip,listenerPort: prot);
            server.ClientConnected += Server_ClientConnected;
            server.ClientDisconnected += Server_ClientDisconnected;
            server.MessageReceived += Server_MessageReceived;
        }

        private void Server_MessageReceived(object sender, MessageReceivedEventArgs e)
        {
            if(e.MessageType == System.Net.WebSockets.WebSocketMessageType.Text)
            {
                CommondWeb web = Encoding.UTF8.GetString(e.Data).ToObject<CommondWeb>();
                OnReceiveWeb.Invoke(e.IpPort,web);
            }
            if (e.MessageType == System.Net.WebSockets.WebSocketMessageType.Binary)
            {
                CommondKey Key = (CommondKey)BitConverter.ToInt32(e.Data, 0);
                int size = BitConverter.ToInt32(e.Data, 4);

                CommondPC cmd = new CommondPC();
                cmd.Key = Key;

                if (size > 0)
                {
                    cmd.Data = new byte[e.Data.Length - 8];
                    Array.Copy(e.Data, 8, cmd.Data, 0, cmd.Data.Length);
                }
                OnReceivePC.Invoke(e.IpPort, cmd);
            }
        }

        private void Server_ClientDisconnected(object sender, ClientDisconnectedEventArgs e)
        {
            if (clients.TryRemove(e.IpPort, out var client))
            {
                OnClose.Invoke(client.IpPort);
            }
        }

        private void Server_ClientConnected(object sender, ClientConnectedEventArgs e)
        {
            ClientInfo clientInfo = new ClientInfo();
            clientInfo.IpPort = e.IpPort;
            clientInfo.HttpRequest = e.HttpRequest;
            clients.AddOrUpdate(clientInfo.IpPort, clientInfo, (k, v) => clientInfo);
            OnAccept.Invoke(clientInfo.IpPort);
        }






        public void Start()
        {
            server.Start();
        }


        public Task SendData(string value, string ipProt)
        {
            try
            {
                if (clients.TryGetValue(ipProt, out var clinet))
                {
                    server.SendAsync(ipProt, value);
                }
                return Task.CompletedTask;
            }
            catch (Exception ex)
            {
                LogHelper.Log(LogDefine.LogError, ex, $"发送数据异常：connectId={ipProt}");
            }
            return Task.CompletedTask;
        }


        public Task SendData<T>(Enum main,T value, string ipProt)
        {
            byte[] body = value?.ToJsonByte();
            return SendData(main, body, ipProt);
        }

        public Task SendData(Enum main,byte[] body, string ipProt)
        {
            List<byte> sendData = new List<byte>();
            sendData.AddRange(BitConverter.GetBytes((int)(object)main));
            if (body != null)
            {
                sendData.AddRange(BitConverter.GetBytes(body.Length));
                sendData.AddRange(body);
            }
            else
            {
                sendData.AddRange(BitConverter.GetBytes(0));
            }
            return SendData(ipProt, sendData.ToArray());
        }

        public void CloseClient(string ipProt)
        {
            try
            {
                if (clients.TryRemove(ipProt, out var clinet))
                {
                    server.DisconnectClient(ipProt);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Log(LogDefine.LogError, ex, $"关闭客户端连接异常：connectId={ipProt}");
            }
        }

        private async Task SendData(string ipProt, byte[] value)
        {
            try
            {
                if (clients.TryGetValue(ipProt, out var clinet))
                {
                    await server.SendAsync(ipProt, value);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Log(LogDefine.LogError, ex, $"发送数据异常：connectId={ipProt}");
            }
        }

        public bool IsExists(string connectId)
        {
            return clients.ContainsKey(connectId);
        }
    }

    public class ClientInfo
    {
        public string IpPort { get; set; }

        public HttpListenerRequest HttpRequest { get; set; }

        public bool IsWeb { get; set; }
    }
}
